package org.fxml.managers {
	import flash.utils.setTimeout;

	/**
	 * @author jordandoczy
	 */
	import org.fxml.utils.Logger;

	import mx.events.ResourceEvent;
	import mx.rpc.AsyncToken;
	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;
	import mx.rpc.soap.LoadEvent;
	import mx.rpc.soap.Operation;
	import mx.rpc.soap.WebService;

	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.utils.Dictionary;
	import flash.utils.Proxy;
	import flash.utils.flash_proxy;

	public class SoapOperationManager extends Proxy implements IEventDispatcher, ISoapOperationManager{
	
		protected var _autoSend:Boolean = false;
		protected var _dictionary:Dictionary = new Dictionary();
		protected var _dispatcher:IEventDispatcher = new EventDispatcher(); 
		protected var _completedRequests:uint = 0;
		protected var _totalRequests:uint = 0;
		protected var _isWorking:Boolean = false;
		protected var _tokens:Dictionary = new Dictionary();
		protected var _webService:WebService;
		protected var _allKeys:Array = [];
		protected var _currentNum:int = 0;
		
		public function SoapOperationManager(webService:WebService){
			trace("SoapOperationManager v.3");
			_webService = webService;
			_webService.addEventListener(LoadEvent.LOAD, onWSDLLoaded, false, 0, true);
		}
		
		override flash_proxy function callProperty(methodName:*, ... args):* {
			return this[methodName].apply(this, args);
	    }
		
		override flash_proxy function getProperty(name:*):*{
			return _dictionary[name] || null;
		}
		
		override flash_proxy function hasProperty(name:*):Boolean{
			return _dictionary[name] != null;
		}
		
		override flash_proxy function setProperty(name:*, value:*):void{
			_dictionary[name]=value;
			
			trace("setting property: " + name + " to: " + value);
			
			if(value is AsyncToken){
				_tokens[value]=name;
				_totalRequests++;
			}
		}
		
		// IEventDispatcher
		public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void{
			_dispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference);
		}
		
		public function dispatchEvent(event:Event):Boolean{
			return _dispatcher.dispatchEvent(event);
		}
		
		public function hasEventListener(type:String):Boolean{
			return _dispatcher.hasEventListener(type);
		}
		
		public function removeEventListener(type:String, listener:Function, useCapture:Boolean=false):void{
			_dispatcher.removeEventListener(type, listener, useCapture);
		}
		
		public function willTrigger(type:String):Boolean{
			return _dispatcher.willTrigger(type);
		}
		
		public function get completedRequests():uint{
			return _completedRequests;
		}
		
		public function get isWorking():Boolean{
			return _isWorking;
		}
		
		public function get totalRequests():uint{
			return _totalRequests;
		}
		
		public function send():void{
			if(_webService.ready) execute();
			else{
				_autoSend = true;
				_webService.loadWSDL();
			}
		}
		
		protected function complete():void{
			dispatchEvent(new Event(Event.COMPLETE));
			_isWorking = false;
		}
		
		protected function execute():void{
			
			_isWorking = true;
			
			var key:String = "";
			var i:uint = 0;
			
			for (key in _dictionary){
				_allKeys.push(key);
			}
			
			for (i=0; i<_allKeys.length; i++){
				key = _allKeys[i];
				trace("execute: " + key);
				if(_dictionary[key] is Operation) executeOperation(_dictionary[key], key);
			}
			
			
			/*
			for (var key:String in _dictionary){
				trace("execute: " + key);
				if(_dictionary[key] is Operation) executeOperation(_dictionary[key], key);
			}
			 * 
			 */
		}
	
		protected function executeOperation(item:Operation, key:String):void{
			
			var op:Operation = Operation(_webService.getOperation(item.name));
			op.arguments = item.arguments;
			op.request = item.request;
			op.resultFormat = item.resultFormat;
			
			_allKeys.push(key);
			
			if(!op.hasEventListener(FaultEvent.FAULT)) op.addEventListener(FaultEvent.FAULT, onFault, false, 0, true);
			if(!op.hasEventListener(ResultEvent.RESULT)) op.addEventListener(ResultEvent.RESULT, onItemComplete, false, 0, true);
			
			Logger.debug(op.request);
			this[key] = op.send();
			
			
		}

		protected function increment():void{
			_completedRequests++;
			if(_completedRequests == _totalRequests) complete();
		}
		
		
		protected function itemFailed(operation:Operation):void{
			Logger.debug("failed");
			increment();
			dispatchEvent(new ResourceEvent(ResourceEvent.ERROR, true, false, 0, 0, operation.request.toString()));
		}
		
		protected  function onItemComplete(event:ResultEvent):void{
			Logger.debug("complete");
			var key:String = _tokens[event.token] || null;
			
			if(key){
				this[key] = event.result;
				increment();	
			}
		}
		
		protected function onFault(event:FaultEvent):void{
			dispatchEvent(new FaultEvent(FaultEvent.FAULT, true, true, event.fault, event.token, event.message));
			itemFailed(Operation(event.target));
			trace("FAILED ON PID ID: ", _allKeys);
		}
		
		protected function onWSDLLoaded(event:LoadEvent):void{
			_webService.removeEventListener(LoadEvent.LOAD, onWSDLLoaded);
			if(_autoSend) send();
			_autoSend = false;
		}
	}
}

